package cn.wolfcode.wolf2w.business.service.impl;

import cn.wolfcode.wolf2w.business.api.RemoteDestinationService;
import cn.wolfcode.wolf2w.business.api.domain.*;
import cn.wolfcode.wolf2w.business.api.factory.RemoteDestinationFallbackFactory;
import cn.wolfcode.wolf2w.business.mapper.*;
import cn.wolfcode.wolf2w.business.query.StrategyQuery;
import cn.wolfcode.wolf2w.business.service.IStrategyService;
import cn.wolfcode.wolf2w.business.util.DateUtil;
import cn.wolfcode.wolf2w.business.vo.ThemeVo;
import cn.wolfcode.wolf2w.common.core.constant.SecurityConstants;
import cn.wolfcode.wolf2w.common.core.context.SecurityContextHolder;
import cn.wolfcode.wolf2w.common.redis.service.RedisService;
import cn.wolfcode.wolf2w.common.redis.util.RedisKeys;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.security.Key;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static org.bouncycastle.asn1.x500.style.RFC4519Style.uid;

/**
 * 攻略Service业务层处理
 * 
 * @author yuhao
 * @date 2025-09-16
 */
@Service
@Transactional
public class StrategyServiceImpl extends ServiceImpl<StrategyMapper,Strategy> implements IStrategyService {


    @Autowired
    private StrategyRankMapper strategyRankMapper;
    @Autowired
    private StrategyMapper strategyMapper;
    @Autowired
    private StrategyThemeMapper strategyThemeMapper;
    @Autowired
    private StrategyCatalogMapper strategyCatalogMapper;
    @Autowired
    private RemoteDestinationService remoteDestinationService;
    @Autowired
    private StrategyContentMapper strategyContentMapper;
    @Autowired
    private StrategyConditionMapper strategyConditionMapper;
    @Autowired
    private RedisService redisService;



@Override
public IPage<Strategy> queryPage(StrategyQuery qo) {
    IPage<Strategy> page = new Page<>(qo.getCurrentPage(), qo.getPageSize());
    QueryWrapper<Strategy> wrapper = new QueryWrapper<>();
    if(qo.getType()!=null){
        if(qo.getType() == 1 || qo.getType() == 2){
            wrapper.eq("dest_id", qo.getRefid());
        }else if(qo.getType() == 3){
            wrapper.eq("theme_id", qo.getRefid());
        }
    }

    wrapper.orderByDesc(qo.getOrderBy());
    return baseMapper.selectPage(page, wrapper);
}

    @Override
    public List<Strategy> queryViewnumTop3(Long destId) {

           return lambdaQuery()
                   .eq(Strategy::getDestId,destId)  //355目的地
                   .orderByDesc(Strategy::getViewnum)  //降序
                   .last("limit 3")         //限定3个
                   .list();                       //获取数据
    }

    @Override
    public void statisRank() {
        //先查攻略国歪国内，在查所有
        List<Strategy>  abroadList= lambdaQuery().eq(Strategy::getIsabroad, 1)
                .orderByDesc(Strategy::getViewnum)//使用getViewnum进行降序
                .last("limit 10")  //限定10个
                .list();

        List<Strategy>  chinaList= lambdaQuery().eq(Strategy::getIsabroad, 0)
                .orderByDesc(Strategy::getViewnum)//使用getViewnum进行降序
                .last("limit 10")  //限定10个
                .list();

        List<Strategy> hotList = lambdaQuery()
                .orderByDesc(Strategy::getViewnum)
                .last("limit 10")
                .list();

        Date now = new Date();
        addRank(abroadList,now,1);
        addRank(chinaList,now,2);
        addRank(hotList,now,3);


    }

    @Override
    public List<ThemeVo> queryThemeVo() {
        QueryWrapper<Strategy> Wrapper = new QueryWrapper<>();//条件构造器,不用lambda是因为sql查询条件中有重命名
        Wrapper.groupBy("theme_name");
        Wrapper.select("theme_name","GROUP_CONCAT(DISTINCT dest_id) ids, GROUP_CONCAT(DISTINCT dest_name) names");
        List<Map<String, Object>> list = baseMapper.selectMaps(Wrapper);//查询回来的结果用map接收，
        // 每个Map对应一条分组后的记录，键是查询时指定的字段名（theme_name、ids、names），值是对应的数据。
        List<ThemeVo> themeVoList=new ArrayList<>();
        for (Map<String, Object> map : list) {
            // 1. 提取主题名称
            String themeName = map.get("theme_name").toString();

            // 2. 提取合并的ids和names，拆分为数组
            String ids = map.get("ids").toString();      // 如 "1,3,5"
            String names = map.get("names").toString();  // 如 "迪士尼,动物园,科技馆"
            String[] idArr = ids.split(",");             // 拆分为 ["1","3","5"]
            String[] namesArr = names.split(",");        // 拆分为 ["迪士尼","动物园","科技馆"]

            List<Destination> dests = new ArrayList<>();
            for (int i = 0; i < idArr.length; i++) {
                Long id = Long.valueOf(idArr[i]);
                String name = namesArr[i];
                Destination destination = new Destination();
                destination.setId(id);
                destination.setName(name);
                dests.add(destination);

            }
            ThemeVo vo = new ThemeVo(themeName, dests);
            themeVoList.add(vo);
        }
        return themeVoList;
    }

    //新增

    @Override
    public void insert(Strategy strategy) {
        StrategyTheme theme = strategyThemeMapper.selectById(strategy.getThemeId());
        strategy.setThemeName(theme.getName());
        StrategyCatalog catalog = strategyCatalogMapper.selectById(strategy.getCatalogId());
        strategy.setCatalogName(catalog.getName());
        strategy.setDestId(catalog.getDestId());
        strategy.setDestName(catalog.getDestName());
        strategy.setCreateTime(new Date());
        strategy.setViewnum(0l);
        strategy.setReplynum(0l);
        strategy.setFavornum(0l);
        strategy.setSharenum(0l);
        strategy.setThumbsupnum(0l);
        Boolean b = remoteDestinationService.isabroad(strategy.getDestId(), SecurityConstants.INNER).getData();
        strategy.setIsabroad(b?1l:0l);
        baseMapper.insert(strategy);
        Long strategyId = strategy.getId();
        strategy.getContent().setId(strategyId);
        strategyContentMapper.insert(strategy.getContent());
    }

    //条件导航

    @Override
    public void statisCondition() {
        System.out.println("开始执行条件导航");

        //查国外
        QueryWrapper<Strategy> wrapper = new QueryWrapper<>();
        wrapper.eq("isabroad", 1);
        wrapper.groupBy("dest_id, dest_name");
        wrapper.select("dest_id refid, dest_name name, COUNT(1) count");
        wrapper.orderByDesc("count");
        List<Map<String, Object>> abroadList = baseMapper.selectMaps(wrapper);

        //查国内
        wrapper.clear();
        wrapper.eq("isabroad", 0);
        wrapper.groupBy("dest_id, dest_name");
        wrapper.orderByDesc("count");
        wrapper.select("dest_id refid, dest_name name, COUNT(1) count");
        List<Map<String, Object>> chinaList = baseMapper.selectMaps(wrapper);

        //查主题
        wrapper.clear();
        wrapper.groupBy("theme_id, theme_name");
        wrapper.select("theme_id refid, theme_name name, count(1) count");
        wrapper.orderByDesc("count");
        List<Map<String, Object>> themeList = baseMapper.selectMaps(wrapper);

        Date now = new Date();

        addCondition(abroadList, now, 1);
        addCondition(chinaList, now, 2);
        addCondition(themeList, now, 3);
    }

    private void addCondition(List<Map<String, Object>> list,  Date now, long type){
        for (Map<String, Object> map : list) {
            Long refid = Long.valueOf(map.get("refid").toString());
            String name = map.get("name").toString();
            Long count = Long.valueOf(map.get("count").toString());
            StrategyCondition condition = new StrategyCondition();
            condition.setCount(count);
            condition.setName(name);
            condition.setRefid(refid);
            condition.setStatisTime(now);
            condition.setType(type);
            strategyConditionMapper.insert(condition);
        }
    }




    public Map<String, Object> viewnumIncrease(Long sid) {
        String key = strategyHashInit(sid);
        //阅读量+1
        redisService.incrementCacheMapValue(key,"viewnum",1);
        //返回数据   避免出现脏数据的问题
        Map<String, Object> cacheMap = redisService.getCacheMap(key);
        return cacheMap;
    }
    //回复量
    @Override
    public Map<String, Object> replynumIncr(Long sid) {
        String key=strategyHashInit( sid);//通过sid确定要操作的 Redis 哈希表，key是这个哈希表在 Redis 中的唯一标识
        redisService.incrementCacheMapValue(key,"replynum",1);//统计该策略的回复数量，每执行一次，回复数加 1
        Map<String, Object> cacheMap = redisService.getCacheMap(key);
        //获取策略的缓存数据，获取key对应的整个 Redis 哈希表数据，并转换为 Java 的Map<String, Object>对象。
        return cacheMap;
    }

    @Override
    public Map<String, Object> favor(Long sid) {
        Long uid = SecurityContextHolder.getUserId();
        //当前用户存储的时候，使用用户id作为key
        String key = RedisKeys.USERR_STRATEGY_FAVOR.join(uid.toString());
        //判断redis中有没有这个key
        if( ! redisService.hasKey( key)){
            Set<Long> sids=new HashSet<>();
            sids.add(-1L);//防止集合被删除
            //如果用户没有收藏过并且redis中没有该用户对应的key
            redisService.setCacheSet(key,sids);
        }
        boolean b=false;
        String hashKey = RedisKeys.STRATEGY_STATIS_HASH.join(sid.toString());
        //看当前集合有没有当前文章
        if(redisService.isCacheSetContains(key,sid)){
            //取消收藏
            redisService.incrementCacheMapValue(hashKey,"favornum",-1);
            redisService.deleteCacheSetValue(key,sid);//移除集合中的数据
        }else{
            //收藏
            redisService.incrementCacheMapValue(hashKey,"favornum",1);
            redisService.addCacheSetValue(key,sid);
            b=true;
        }
        Map<String, Object> cacheMap = redisService.getCacheMap(hashKey);
        cacheMap.put("result",b);
        return cacheMap;
    }

    @Override
    public Boolean isUserFavor(Long uid, Long sid) {
        String key = RedisKeys.USERR_STRATEGY_FAVOR.join(uid.toString());
        //判断redis中有没有这个key
        if( ! redisService.hasKey( key)){
            Set<Long> sids=new HashSet<>();
            sids.add(-1L);//防止集合被删除
            //如果用户没有收藏过并且redis中没有该用户对应的key
            redisService.setCacheSet(key,sids);
        }
        //判断当前用户是否收藏过
        return redisService.isCacheSetContains(key,sid);
    }

    //持久化
    @Override
    public void strategyStatisHashPersistence() {
//        System.out.println("开始持久化");
        String hashKey = RedisKeys.STRATEGY_STATIS_HASH.join("*");
        Collection<String> keys = redisService.keys(hashKey);
        if(keys!=null && keys.size()>0){

            for (String key : keys) {
                Map<String, Object> cacheMap = redisService.getCacheMap(key);
                lambdaUpdate().eq(Strategy::getId, cacheMap.get("id"))
                        .set(Strategy::getViewnum, cacheMap.get("viewnum"))
                        .set(Strategy::getReplynum, cacheMap.get("replynum"))
                        .set(Strategy::getFavornum, cacheMap.get("favornum"))
                        .set(Strategy::getThumbsupnum, cacheMap.get("thumbsupnum"))
                        .set(Strategy::getSharenum, cacheMap.get("sharenum"))
                        .update();
            }
        }
    }



    @Override
    public Map<String, Object> thumbsup(Long sid) {
        //注入uid
        Long uid = SecurityContextHolder.getUserId();
        //拼key   面试会问这样拼接会不会很长，回答:并不是每个人都会点赞，不会有太多
        String key = RedisKeys.USER_STRATEGY_THUMBSUP.join(sid.toString(), uid.toString());
        //计算过期时间(工具类)
        Date now = new Date();
        Date end = DateUtil.getEndDate(now);
        long seconds = DateUtil.getDateBetween(now, end);//结果取绝对值
        //判断redis中是否有key， 如果没有创建key设置值0
        if( ! redisService.hasKey(key)){
            redisService.setCacheObject(key, 0, seconds, TimeUnit.SECONDS);
        }
        //点赞次数++（操作数）
        Long ret = redisService.incrementCacheObjectValue(key, 1);
        String hashKey = RedisKeys.STRATEGY_STATIS_HASH.join(sid.toString());
        //判断点赞次数，如果大于3，设置result为false
        boolean result = false;
        //如果小于等于3，设置result为true
        if(ret <= 3){
            result = true;
            //每天只能点的次数
            redisService.incrementCacheMapValue(hashKey, "thumbsupnum", 1);
        }
        //查询统计数字map结构，并将result加入到map中
        Map<String, Object> cacheMap = redisService.getCacheMap(hashKey);
        cacheMap.put("result", result);
        //返回map
        return cacheMap;
    }

    //阅读量
    private String strategyHashInit(Long sid) {
        String key = RedisKeys.STRATEGY_STATIS_HASH.join(sid.toString());
        if( ! redisService.hasKey(key)){  //文章第一次被访问
            //面试中需要添加布隆过滤器，如果布隆过滤器返回mysql中有数据，则从mysql中查，否则不查
            //不添加布隆过滤器会产生缓存击穿
            Strategy strategy = this.getById(sid); //到mysql中查,当第一次或则redis宕机了，redis中 就没有数据了，以mysql为准
            Map<String, Object> map = new HashMap<>();
            map.put("viewnum",Integer.valueOf(strategy.getViewnum().toString()));//redis中不能long类型的数据进行++，所以要转成int
            map.put("replynum",Integer.valueOf(strategy.getReplynum().toString()));
            map.put("sharenum",Integer.valueOf(strategy.getSharenum().toString()));
            map.put("favornum",Integer.valueOf(strategy.getFavornum().toString()));
            map.put("thumbsupnum",Integer.valueOf(strategy.getThumbsupnum().toString()));
            map.put("id",strategy.getId());  //用来说明这篇攻略有这个统计数据
            redisService.setCacheMap(key,map);  //把数据放到redis中，key是前缀加sid
        }
        return key;
    }







    private void addRank(List<Strategy> list,Date  now,long type){
        for (Strategy strategy : list) {
            StrategyRank rank = new StrategyRank();
            rank.setStrategyId(strategy.getId());
            rank.setStrategyTitle(strategy.getTitle());
            rank.setDestName(strategy.getDestName());
            rank.setDestId(strategy.getDestId());
            rank.setStatisnum(strategy.getViewnum());
            rank.setStatisTime( now);
            rank.setType( type);
            strategyRankMapper.insert(rank);
        }
    }

}
